/*!
  \file
  Allocator which uses posix_memalign
*/

#ifndef SCIGPU_HHV_ALIGNED_ALLOCATOR_HPP
#define SCIGPU_HHV_ALIGNED_ALLOCATOR_HPP

#include <cstdlib>
#include <new>

// ==============================


/*
  The core of this code was copied from
  http://www.drdobbs.com/184403759
*/

namespace SciGPU {
  namespace HHV {

    //! A template allocator for aligned data
    template <typename T, size_t alignment> class aligned_allocator {
    public:
      typedef T                 value_type;
      typedef value_type*       pointer;
      typedef const value_type* const_pointer;
      typedef value_type&       reference;
      typedef const value_type& const_reference;
      typedef std::size_t       size_type;
      typedef std::ptrdiff_t    difference_type;
  
      template <typename U> 
      struct rebind {
        typedef aligned_allocator<U,alignment> other;
      };

      //! Default constructor
      aligned_allocator( void ) {}
      //! Copy Constructor
      aligned_allocator( const aligned_allocator& src ) {}
      //! Constructor from different allocator
      template <typename U> 
      aligned_allocator( const aligned_allocator<U,alignment>& src ) {}

      //! Destructor
      ~aligned_allocator() {}

      //! Method to return address
      pointer address( reference x ) const {
        return &x;
      }
      //! Method to return a constant address
      const_pointer address( const_reference x ) const { 
        return x;
      }

      //! Allocation method
      pointer allocate( size_type n, const_pointer = 0 ) {
        void* p;
        int err = posix_memalign( &p, alignment, n*sizeof(T) );
        if( err ) {
          throw std::bad_alloc();
        }
        return( static_cast<pointer>(p) );
      }

      //! Deallocation method
      void deallocate( pointer p, size_type s ) {
        s = 10; // Silence compiler
        free( p );
      }

      size_type max_size( void ) const { 
        return( static_cast<size_type>(-1) / sizeof(T) );
      }

      //! Create a new object at location
      void construct( pointer p, const value_type& x ) { 
        new(p) value_type(x); 
      }

      //! Destroy an object
      void destroy( pointer p ) {
        p->~value_type();
      }

    private:
      //! Copy operator (inhibited)
      void operator=(const aligned_allocator& src);
    };


    //! Specialisation for voids
    template<size_t alignment> class aligned_allocator<void,alignment> {
      typedef void        value_type;
      typedef void*       pointer;
      typedef const void* const_pointer;

      template <class U> 
      struct rebind { typedef aligned_allocator<U,alignment> other; };
    };


    //! Equality comparison
    template <typename T, size_t a1, size_t a2>
    inline bool operator==( const aligned_allocator<T,a1>& a, 
                            const aligned_allocator<T,a2>& b ) {
      return( a1 == a2 );
    }

    //! Inequality comparison
    template <typename T, size_t a1, size_t a2>
    inline bool operator!=(const aligned_allocator<T,a1>& a, 
                           const aligned_allocator<T,a2>& b ) {
      return( !(a==b) );
    }
    
  }
}

#endif
